###########################################################################
# $Id: labourJSON.p 39 2009-06-24 17:19:17Z donbeave $
#  based on JSHttpRequest class by Misha v.3
#   (http://www.parser.ru/examples/js_http_request/)
#  decoders based on parser-json class by zhizhilkin
#   (http://code.google.com/p/parser-json/)
###########################################################################

@CLASS
labourJSON

@USE
pf/types/pfClass.p

@OPTIONS
locals

#----- Public -----

@encode[altData]
# по умолчанию для пустой строки или неизвестных объектов выдается null
  $result[null]

  ^switch[$altData.CLASS_NAME]{
    ^case[string]{
      ^if(def $altData){
        $result[^_encodeString[$altData]]
      }
    }
    ^case[int]{
      $result[^_encodeInt($altData)]
    }
    ^case[double]{
      $result[^_encodeDouble[$altData]]
    }
    ^case[bool]{
      ^if($altData){
        $result[true]
      }{
         $result[false]
       }
    }
    ^case[table]{
      ^if($altData){
        $result[^_encodeTable[$altData]]
      }
    }
    ^case[hash]{
      ^if($altData){
        $result[^_encodeHash[$altData]]
      }
    }
    ^case[date]{
      $result[^_encodeDate[$altData]]
    }
    ^case[xdoc]{
      $result[^_encodeXDoc[$altData]]
    }

    ^case[DEFAULT]{
      ^if(def $altData && $altData.__asJSON is junction){
        $result[^altData.__asJSON[]]
      }{
        ^if($altData is pfCollection){
          $result[^_encodeCollection[$altData]]
        }
      }
    }
  }

#@expiremental
# спотыкается на кавычках и еще некоторых мелочах
@decode[sText]
  $self.sJson[$sText]
  $result[^_decode[]]

#----- Private -----

@_encodeString[strData]
  $strData[^strData.match[\r\n][g]{^#0A}]

  $result["^taint[js][$strData]"]

@_encodeInt[intData]
  $result[^_encodeDouble[$intData]]

@_encodeDouble[dblData]
  $result[$dblData]

@_encodeHash[hshData]
  $bIsHashOfBool(true)
# check if our hash is hash of bool with only 'true' values
  ^hshData.foreach[sKey;aValue]{
    ^if($bIsHashOfBool && !($aValue is bool && $aValue)){
      $bIsHashOfBool(false)
    }
  }

  ^if($bIsHashOfBool){
    $result[[
      ^hshData.foreach[sKey;]{
        ^_encodeString[$sKey]
      }[, ]
    ]]
  }{
    ^rem{ *** in other case return associated array *** }
    $result[{
      ^hshData.foreach[sKey;aValue]{
        ^self._encodeString[$sKey]:
        ^if($aValue is double || $aValue is bool || $aValue is int){
          ^encode($aValue)
        }{
          ^encode[$aValue]
        }
      }[, ]
    }]
  }

@_encodeTable[tblData]
# не работает с nameless таблицами, т.к. они считаются устаревшими
  $tKeys[^tblData.columns[]]
  ^if($tKeys){
    $result[[
      ^tblData.menu{
        {
          ^tKeys.menu{
            ^_encodeString[$tKeys.column]:^_encodeString[$tblData.[$tKeys.column]]
          }[, ]
        }
      }[, ]
    ]]
  }{
     $result[null]
   }

@_encodeDate[dtData]
  $result[new Date(^dtData.unix-timestamp[]000)]

@_encodeXDoc[xdcData]
  $result[^_encodeString[^xdcData.string[]]]

@_encodeCollection[objCollection]
  $result[[
    ^if($objCollection){
      ^objCollection.foreach[oItem]{
        ^encode[$oItem]
      }[,]
    }
  ]]


@_decode[sJson][ch;hR]
  $self.sJson[^self.sJson.trim[]]
  $ch[^self.sJson.left(1)]

  $result[]

  ^switch[$ch]{
    ^case[^{]{
      $result[^_decodeObject[]]
    }
    ^case[^[]{
      $result[^_decodeArray[]]
    }
    ^case[";']{
      $result[^_decodeString[$ch]]
    }
    ^case[-]{
      $result[^_decodeNumber[]]
    }
    ^case[DEFAULT]{
      ^rem{ отрицательные числа? проверяй регуляркой }
      ^if(^ch.int(-1) >= 0){
        $result(^_decodeNumber[])
      }{
        $result[^_decodeWord[]]
      }
    }
  }

@_decodeObject[][sJson;key;iIndex;hR]
  $self.sJson[, ^self.sJson.trim[start;^{]]
  $self.sJson[^self.sJson.trim[]]

  $hR[^hash::create[]]

  # найти ключи и декодировать объекты по очереди
  $iIndex(0)
  ^while(^self.sJson.length[] > 0){
    $key[^self.sJson.match[^^\s*,\s*
      "?(\w+)"?
      \s*\:(.*)][gx]]

    ^if(def $key.2){
      $self.sJson[$key.2]
      $hR.[$key.1][^_decode[]]
    }{
      $self.sJson[^self.sJson.trim[]]
      $self.sJson[^self.sJson.trim[both;^}]]
      ^break[]
    }

    ^iIndex.inc[]
  }

  $result[$hR]

@_decodeArray[][sJson;tRx;iIndex;hR]
  $hR[^hash::create[]]

  $self.sJson[^self.sJson.trim[]]
  $self.sJson[^self.sJson.trim[start;^[]]
  $self.sJson[^self.sJson.trim[]]

  # FIXME: тут некоторая натяжка с пустыми массивами...
  ^if(^self.sJson.left(1) eq ]){
    $self.sJson[^self.sJson.trim[start;^]]]
  }{
    $self.sJson[, $self.sJson]
    $iIndex(0)
    ^while(^self.sJson.length[] > 1){
      $tRx[^self.sJson.match[^^\s*,\s*(.*)][g]]

      ^if(def $tRx.1){
        $self.sJson[$tRx.1]
        $hR.[$iIndex][^_decode[]]
      }{
        $self.sJson[^self.sJson.trim[]]
        $self.sJson[^self.sJson.trim[both;^]]]
        ^break[]
      }
      ^iIndex.inc[]
    }
  }

  $result[$hR]

@_decodeString[sQuote][sJson;sString;tMtc;hR]
  $self.sJson[^self.sJson.trim[start;$sQuote]]

  # найти неэкранированную закрывающую кавычку
  $tMtc[^self.sJson.match[(.*?[^^\\])\$sQuote][]]
  $sString[$tMtc.1]

  # если нет закрывашки - ошибка синтаксиса

  $sRemainder[^self.sJson.right(^self.sJson.length[] - ^sString.length[] - 1)]

  $self.sJson[$sRemainder]
  $result[$sString]

@_decodeNumber[][sJson;sString;tMtc;hR]
  $tMtc[^self.sJson.match[(\d+)][]]
  $sString[$tMtc.1]

  # если ничего не нашлось - ошибка синтаксиса

  $sRemainder[^self.sJson.right(^self.sJson.length[] - ^sString.length[])]

  $self.sJson[$sRemainder]

  $result($sString)

@_decodeWord[][sJson;sString;tMtc;hR]
  $tMtc[^self.sJson.match[(\w+)][]]
  $sString[$tMtc.1]

  # если ничего не нашлось - ошибка синтаксиса

  $sRemainder[^self.sJson.right(^self.sJson.length[] - ^sString.length[])]

  $self.sJson[$sRemainder]
  $result[$sString]